package com.hzw.saas.web.app.user.controller;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.hzw.saas.api.user.IUserAccountService;
import com.hzw.saas.api.user.IUserTripartiteService;
import com.hzw.saas.api.user.bo.UserAccountBO;
import com.hzw.saas.api.user.bo.UserWeTripartiteBO;
import com.hzw.saas.api.user.query.UserAccountAddQuery;
import com.hzw.saas.api.user.query.UserAccountGetQuery;
import com.hzw.saas.common.config.annotation.SysLog;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.verify.service.IVerifyService;
import com.hzw.saas.web.app.user.dto.TokenInfoDto;
import com.hzw.saas.web.app.user.param.UserRegisterParam;
import io.swagger.annotations.*;
import lombok.AllArgsConstructor;
import ma.glasnost.orika.MapperFacade;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @author zzl
 * @since 12/23/2020
 */
@Controller
@RequestMapping("/user")
@Validated
@AllArgsConstructor
@Api(tags = "用户中心/用户注册")
public class RegisterController {

    private final MapperFacade mapperFacade;
    private final IUserAccountService userAccountService;
    private final IVerifyService verifyService;
    // 查询用户逻辑
    @Resource
    private UserDetailsService userDetailsService;


    @Resource
    private IUserTripartiteService userWeChatService;

    @Resource
    private AuthorizationServerTokenServices userTokenServices;

    @PostMapping("/register")
    @SysLog(operation = "提交注册", mode = 3)
    @ApiOperation(value = "提交注册", notes = "处理用户注册请求")
    @ApiResponses({
        @ApiResponse(code = 200, message = "注册成功,true"),
        @ApiResponse(code = 400, message = "用户已存在/验证码错误")
    })
    @ApiOperationSort(1)
    public ResponseEntity<Object> register(@RequestBody UserRegisterParam userRegister) {
        AssertUtil.assertThrow("手机和邮箱不能同时为空", StrUtil.isAllBlank(userRegister.getPhoneNo(), userRegister.getEmail()));
        boolean verifySms = false;
        boolean verifyEmail = false;
        // 检查验证码
        if (StringUtils.isNotBlank(userRegister.getPhoneNo())) {
            verifySms = verifyService.verify(userRegister.getPhoneNo(), userRegister.getVerifyCode(), false);
        }
        if (StringUtils.isNotBlank(userRegister.getEmail())) {
            verifyEmail = verifyService.verify(userRegister.getEmail(), userRegister.getVerifyCode(), false);
        }
        AssertUtil.assertThrow("验证码错误", !verifySms && !verifyEmail);

        UserAccountAddQuery userAccount = mapperFacade.map(userRegister, UserAccountAddQuery.class);
        Date now = new Date();
        if (verifySms) {
            userAccount.setPhoneVerifyDate(now);
            userAccount.setEmail(null);
        } else {
            userAccount.setEmailVerifyDate(now);
            userAccount.setPhoneNo(null);
        }

        UserAccountBO userAccountBO = userAccountService.addAccount(userAccount, true);

        if (StringUtils.isNotBlank(userRegister.getPhoneNo())) {
            verifyService.delete(userRegister.getPhoneNo());
        }
        if (StringUtils.isNotBlank(userRegister.getEmail())) {
            verifyService.delete(userRegister.getEmail());
        }
        //如果openId和unionId都不为空，默认视为三方注册绑定用户信息，只要不是信息已被重复使用都不影响注册业务
        if (StrUtil.isNotBlank(userRegister.getOpenId())||StrUtil.isNotBlank(userRegister.getUnionId())){
            List<UserWeTripartiteBO> byUnionIdAndOpenId = userWeChatService.getByUnionIdAndOpenId(userRegister.getUnionId(), userRegister.getOpenId(), "");
            if (CollectionUtil.isNotEmpty(byUnionIdAndOpenId)){
                UserWeTripartiteBO userWeTripartiteBO = byUnionIdAndOpenId.get(0);
                AssertUtil.assertThrow("账户已被绑定，请找回密码",StrUtil.isNotBlank(userWeTripartiteBO.getUserId()));
                userWeTripartiteBO.setUserId(userAccountBO.getUserId());
                userWeChatService.updateById(userWeTripartiteBO);
            }
        }
        return ResponseEntity.ok(true);
    }

    @PostMapping("/register/login")
    @SysLog(operation = "提交注册,返回token", mode = 3)
    @ApiOperation(value = "提交注册,返回token", notes = "处理用户注册请求")
    @ApiResponses({
        @ApiResponse(code = 200, message = "注册成功,true,返回token"),
        @ApiResponse(code = 400, message = "用户已存在/验证码错误")
    })
    @ApiOperationSort(1)
    public ResponseEntity<TokenInfoDto> registerLogin(@RequestBody UserRegisterParam userRegister) {
        ResponseEntity<Object> register = register(userRegister);
        Object body = register.getBody();
        TokenInfoDto tokenInfoDto = new TokenInfoDto();
        if (body.equals(true)) {
            UserAccountBO userAccountBO = new UserAccountBO();
            if (userRegister.getPhoneNo() == null || "".equals(userRegister.getPhoneNo())){
                userAccountBO =  userAccountService.getByEmail(userRegister.getEmail());
            }
            if (userRegister.getEmail() == null || "".equals(userRegister.getEmail())){
                userAccountBO =  userAccountService.getAccountByPhone(userRegister.getPhoneNo());
            }
            tokenInfoDto = buildTokenInfo(userAccountBO);
        }
        return ResponseEntity.ok(tokenInfoDto);
    }


    @GetMapping("/register/username")
    @SysLog
    @ApiOperation(value = "生成用户名", notes = "生成系统默认的用户名")
    @ApiOperationSort(2)
    public ResponseEntity<String> generateUsername() {
        long l = System.nanoTime();
        return ResponseEntity.ok(String.valueOf(l));
    }

    @GetMapping("/exists/username/{username}")
    @SysLog
    @ApiOperation(value = "验证用户名", notes = "验证用户名是否重复")
    @ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String")
    @ApiOperationSort(2)
    public ResponseEntity<Boolean> existsUsername(@PathVariable String username) {
        boolean exists = userAccountService.existAccount(new UserAccountGetQuery().setUserName(username));
        return ResponseEntity.ok(exists);
    }

    @GetMapping("/exists/phone/{phone}")
    @SysLog
    @ApiOperation(value = "验证手机号", notes = "验证手机号是否重复")
    @ApiImplicitParam(name = "phone", value = "手机号", required = true, dataType = "String")
    @ApiOperationSort(3)
    public ResponseEntity<Boolean> existsPhoneNo(@PathVariable String phone) {
        boolean exists = userAccountService.existAccount(new UserAccountGetQuery().setPhoneNo(phone));
        return ResponseEntity.ok(exists);
    }

    @GetMapping("/exists/email/{email}")
    @SysLog
    @ApiOperation(value = "验证邮箱", notes = "验证邮箱是否重复")
    @ApiImplicitParam(name = "email", value = "邮箱", required = true, dataType = "String")
    @ApiOperationSort(4)
    public ResponseEntity<Boolean> existsEmail(@PathVariable String email) {
        boolean exists = userAccountService.existAccount(new UserAccountGetQuery().setEmail(email));
        return ResponseEntity.ok(exists);
    }

    private TokenInfoDto buildTokenInfo(UserAccountBO account){
        UserDetails user = userDetailsService.loadUserByUsername(account.getUserName());
        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, account.getPassword(), user.getAuthorities());
        TokenRequest tokenRequest = new TokenRequest(null, null, Collections.singletonList("all"), null);
        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(new BaseClientDetails());
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
        OAuth2AccessToken oAuth2AccessToken = userTokenServices.createAccessToken(oAuth2Authentication);
        TokenInfoDto tokenInfo = new TokenInfoDto();
        tokenInfo.setAccessToken(oAuth2AccessToken.getValue());
        tokenInfo.setExpiresIn(oAuth2AccessToken.getExpiresIn());
        tokenInfo.setTokenType("bearer");
        tokenInfo.setRefreshToken(oAuth2AccessToken.getRefreshToken().getValue());
        account.setPassword("");
        tokenInfo.setUserAccountBO(account);
        tokenInfo.setBind(true);
        return tokenInfo;
    }

}
